package cn.trigram.thread;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;

/**
 * 来自 {@link AbstractQueuedSynchronizer} 文档注释中的案例。
 * <br/>
 * 基于独占模式完成的互斥锁
 */
public class Mutex implements Lock, java.io.Serializable {


  /**
   * 根据AQS建议，子类应当定义为非公共的内部帮助类
   */
  private static class Sync extends AbstractQueuedSynchronizer {

    /**
     * 报告是不是被锁定状态
     */
    @Override
    protected boolean isHeldExclusively() {

      return getState() == 1;
    }

    /**
     * 如果状态值为0，则获取锁
     */
    @Override
    public boolean tryAcquire(int acquires) {

      assert acquires == 1; // 限定必须参数使用1
      if (compareAndSetState(0, 1)) {
        setExclusiveOwnerThread(Thread.currentThread());
        return true;
      }
      return false;
    }

    /**
     * 通过设置状态值为0来释放锁
     */
    @Override
    protected boolean tryRelease(int releases) {

      assert releases == 1; // 限定必须参数使用1
      if (getState() == 0) {
        throw new IllegalMonitorStateException();
      }
      setExclusiveOwnerThread(null);
//      这里确保了是只有互斥，所以直接使用set方法就可以。也可改为compareAndSetState(1, 0)
      setState(0);
      return true;
    }

    /**
     * 提供一个条件类
     */
    Condition newCondition() {

      return new ConditionObject();
    }


    /**
     * 反序列化属性，这里其实不应该重置状态。
     * 同时在反序列化中，线程队列是不可被序列化的。
     */
    private void readObject(ObjectInputStream s)
        throws IOException, ClassNotFoundException {

      s.defaultReadObject();
      setState(0); // 重围为无锁状态
    }

  }

  // 同步对象完成了这些同步工作，这里我们只是同步互斥锁方法代理给了同步对象
  private final Sync sync = new Sync();

  public void lock() {

    sync.acquire(1);
  }

  public boolean tryLock() {

    return sync.tryAcquire(1);
  }

  public void unlock() {

    sync.release(1);
  }

  public Condition newCondition() {

    return sync.newCondition();
  }

  public boolean isLocked() {

    return sync.isHeldExclusively();
  }

  public boolean hasQueuedThreads() {

    return sync.hasQueuedThreads();
  }

  /**
   * 可被中断的获取锁方法
   */
  public void lockInterruptibly() throws InterruptedException {

    sync.acquireInterruptibly(1);
  }

  public boolean tryLock(long timeout, TimeUnit unit)
      throws InterruptedException {

    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
  }

}

